package com.hk.controller;

import cn.hutool.core.lang.UUID;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.annotation.Lock4j;
import com.google.common.collect.Lists;
import com.hk.service.business.cache.RedisBusiness;
import com.hk.utils.R;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author: dpb
 * @date: 2022/12/5
 */
@Slf4j
@RestController
@RequestMapping("/redis")
public class RedisController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private LockTemplate lockTemplate;

    @Autowired
    private Redisson redisson;

    @Autowired
    private RedisBusiness redisBusiness;

    @GetMapping("/setData")
    public R testRedis() {
        redisTemplate.opsForValue().set("key", "zs");
        return R.ok();
    }

    @GetMapping("/getData")
    public R getData() {
        Object key = redisTemplate.opsForValue().get("key");
        return R.ok().put("data", key);
    }

    @ApiModelProperty("redisson 实现分布式锁")
    @GetMapping("redissonLock")
    public R redissonLock() {
        return redisBusiness.redissonLock();
    }

    @ApiOperation("lock4j-Redisson 实现分布式锁")
    @GetMapping("lock4jLock")
    public R lock4jLock() {
        return redisBusiness.lock4jLock();
    }


    @ApiOperation("redisTemplate 实现分布式锁")
    @GetMapping("/lock")
    public R redisLock() {
        return redisBusiness.redisLock();
    }

    // 用户在5秒内只能访问1次
//    @Lock4j(keys = {"#user.id"}, acquireTimeout = 0, expire = 5000, autoRelease = false)
    @Lock4j(keys = {"#id"}, acquireTimeout = 0, expire = 5000, autoRelease = false)
    @ApiOperation("lock4j接口限流访问")
    @GetMapping("lock4jLimit")
    public R lock4jLimit(String id) {
        log.info("业务执行中~");
        return R.ok();
    }

    @ApiOperation("lua脚本实现接口访问限流")
    @GetMapping("/submit")
    public R submit(){
        String key = "user_id";
        Integer expire = 3;
        ArrayList<String> keys = Lists.newArrayList(key);
        String result = stringRedisTemplate.execute(setNxWithExpireTime, keys, expire.toString());
        log.info("result:{}", result);
        boolean flag = !"OK".equalsIgnoreCase(result);
        return R.ok().setData(flag);
    }

    private RedisScript<String> setNxWithExpireTime = new DefaultRedisScript<>(
            "return redis.call('set', KEYS[1], 1, 'ex', ARGV[1],'nx');",
            String.class
    );

    @ApiOperation("redis滑动窗口实现限流")
    @GetMapping("/limitFlow")
    public R limitFlow() {
        Date date = new Date();
        Long currentTime = date.getTime();
        System.out.println(currentTime);
        if (redisTemplate.hasKey("limit")) {
            Long intervalTime = 60 * 1000L;
            Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime - intervalTime, currentTime).size();        // intervalTime是限流的时间
//            Integer count = redisTemplate.opsForZSet().rangeByScore("limit", intervalTime -  currentTime, intervalTime).size();        // intervalTime是限流的时间
            System.out.println(count);
            if (count != null && count > 5) {
                return R.ok("每分钟最多只能访问5次");
            }
        }
        redisTemplate.opsForZSet().add("limit", UUID.randomUUID().toString(), currentTime);
        return R.ok("访问成功");
    }

    @ApiOperation("令牌桶限流")
    @GetMapping("/tokenBucket")
    public R testTokenBucket() {
        RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
        // 最大流速 = 每10秒钟产生1个令牌
        rateLimiter.trySetRate(RateType.OVERALL, 1, 10, RateIntervalUnit.SECONDS);
        //需要1个令牌
        if (rateLimiter.tryAcquire(1)) {
            return R.ok("令牌桶里面有可使用的令牌");
        }
        return R.error("不好意思，请过十秒钟再来~~~~~~~");
    }


}
